﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using NSoup.Nodes;

namespace CrazyReader.Core.Crawl
{
    /// <summary>
    /// Web请求任务的基类
    /// </summary>
    public abstract class WebTaskBase
    {
        private object addCacheSyncObj = new object();
        private object completedCacheSyncObj = new object();

        #region Property

        /// <summary>
        /// 是否完成
        /// </summary>
        public bool IsCompleted { get; protected set; }

        /// <summary>
        /// 用户自定义字段
        /// </summary>
        public object Tag { get; set; }

        /// <summary>
        /// 是否暂停
        /// </summary>
        public bool IsStop { get; protected set; }

        /// <summary>
        /// 当前读取的数据长度，单位K，这个为非精确值
        /// </summary>
        protected int Current { get; set; }

        /// <summary>
        /// 所有的异步WebRequest缓存
        /// </summary>
        protected List<WebRequestCache> requestCache = new List<WebRequestCache>();

        /// <summary>
        /// 已完成的WebRequest缓存
        /// </summary>
        protected List<WebRequestCache> completedCache = new List<WebRequestCache>();

        #endregion

        #region Event

        /// <summary>
        /// 任务完成事件
        /// </summary>
        public event EventHandler<TaskFinishedEventArgs> TaskFinished;

        /// <summary>
        /// 触发搜索结束事件
        /// </summary>
        /// <param name="isCompleted"></param>
        /// <param name="reason"></param>
        protected void RaiseTaskFinishedEvent(bool isCompleted, String reason)
        {
            this.IsCompleted = isCompleted;

            EventHandler<TaskFinishedEventArgs> temp = TaskFinished;
            if (temp != null)
            {
                TaskFinishedEventArgs args = new TaskFinishedEventArgs();
                args.IsCompleted = isCompleted;
                args.Reason = reason;
                temp(this, args);
            }
        }

        /// <summary>
        /// 下载进度改变事件
        /// </summary>
        public event EventHandler<DownloadProgressChangedEventArgs> DownloadProgressChanged;

        /// <summary>
        /// 触发进度改变事件
        /// </summary>
        /// <param name="curent"></param>
        /// <param name="total"></param>
        protected void RaiseDownloadProgressChangedEvent(int curent, int total)
        {
            EventHandler<DownloadProgressChangedEventArgs> temp = DownloadProgressChanged;
            if (temp != null)
            {
                DownloadProgressChangedEventArgs args = new DownloadProgressChangedEventArgs();
                args.Current = curent;
                args.Total = total;
                temp(this, args);
            }
        }

        #endregion

        #region Method

        /// <summary>
        /// 开始任务
        /// </summary>
        public void Start()
        {
            try
            {
                requestCache.Clear();
                completedCache.Clear();
                OnStart();
            }
            catch (Exception e)
            {
            }
        }

        public virtual void OnStart()
        { }

        /// <summary>
        /// 停止搜索
        /// </summary>
        /// <returns>是否完成搜索</returns>
        public virtual void Stop()
        {
            if (!this.IsStop)
            {
                this.IsStop = true;
                //把缓存中的所有未完成的请求都暂停
                try
                {
                    lock (addCacheSyncObj)
                    {
                        foreach (var cache in requestCache)
                        {
                            if (!completedCache.Contains(cache))
                            {
                                cache.IsStop = true;
                                cache.Request.Abort();
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                }
                if (GetSuccessTaskCount() < requestCache.Count)
                {
                    RaiseTaskFinishedEvent(false, "用户中止搜索");
                }
            }
        }

        /// <summary>
        /// 添加WebRequest缓存
        /// </summary>
        /// <param name="cache"></param>
        protected void AddRequestCache(WebRequestCache cache)
        {
            lock (addCacheSyncObj)
            {
                requestCache.Add(cache);
            }
        }

        /// <summary>
        /// 添加已完成的WebRequest缓存
        /// </summary>
        /// <param name="cache"></param>
        protected void AddCompletedCache(WebRequestCache cache)
        {
            lock (completedCacheSyncObj)
            {
                completedCache.Add(cache);
                if (requestCache.Count == GetSuccessTaskCount())
                {
                    RaiseTaskFinishedEvent(true, null);
                }
            }
        }

        /// <summary>
        /// 从流中读取数据，并对外提供进度
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        protected byte[] ReadStream(Stream input, long length)
        {
            byte[] data = null;
            try
            {
                using (input)
                {
                    using (MemoryStream ms = new MemoryStream())
                    {
                        byte[] buffer = new byte[1024];

                        int count = input.Read(buffer, 0, buffer.Length);
                        ms.Write(buffer, 0, count);
                        Current = 1;
                        int total = (int)(length / 1024);
                        if (length % 1024 > 0)
                            total++;
                        RaiseDownloadProgressChangedEvent(Current, total);

                        while (count > 0)
                        {
                            count = input.Read(buffer, 0, buffer.Length);
                            ms.Write(buffer, 0, count);
                            RaiseDownloadProgressChangedEvent(Current, total);
                            Current++;
                        }

                        data = ms.ToArray();
                    }
                }
            }
            catch (Exception e)
            {
            }
            return data;
        }

        /// <summary>
        /// 查找已成功完成的任务数量
        /// </summary>
        /// <returns></returns>
        protected int GetSuccessTaskCount()
        {
            int count = 0;
            lock (completedCacheSyncObj)
            {
                completedCache.ForEach(cache => { if (!cache.IsStop)count++; });
            }
            return count;
        }

        /// <summary>
        /// 开始进行Web请求，并获取反馈
        /// </summary>
        /// <param name="url"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        protected WebRequestCache BeginGetResponse(String url, AsyncCallback callback)
        {
            WebRequestCache cache = new WebRequestCache();
            AddRequestCache(cache);
            WebRequest webRequest = WebRequest.Create(url);
            cache.Request = webRequest;
            cache.Callback = callback;
            cache.Result = webRequest.BeginGetResponse(new AsyncCallback(OnAsyncCallback), cache);
            return cache;
        }

        /// <summary>
        /// 开始进行Web请求，并获取反馈
        /// </summary>
        /// <param name="url"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        protected WebRequestCache BeginGetResponse(String url, String encoding, Action<Document> callback)
        {
            return BeginGetResponse(url, target =>
            {
                WebRequestCache cache = target.AsyncState as WebRequestCache;
                var resp = cache.Request.EndGetResponse(target);
                if (resp.ContentType.ToLower().Contains("utf-8"))
                    encoding = "utf-8";
                // 读取数据并指示进度
                byte[] data = ReadStream(resp.GetResponseStream(), resp.ContentLength);
                if (data != null)
                {
                    NSoup.Nodes.Document doc = NSoup.NSoupClient.Parse(data, encoding);
                    if (callback != null)
                    {
                        try
                        {
                            callback(doc);
                        }
                        catch (Exception e)
                        {
                        }
                    }
                }
            });
        }

        /// <summary>
        /// web请求完成的异步回调
        /// </summary>
        /// <param name="result"></param>
        protected virtual void OnAsyncCallback(IAsyncResult result)
        {
            WebRequestCache cache = result.AsyncState as WebRequestCache;
            if (cache != null && !cache.IsStop)
            {
                if (cache.Callback != null)
                {
                    try
                    {
                        cache.Callback(result);
                    }
                    catch (Exception e)
                    {
                    }
                }
                AddCompletedCache(cache);
            }
        }

        #endregion
    }

    /// <summary>
    /// 任务完成事件
    /// </summary>
    public class TaskFinishedEventArgs : EventArgs
    {
        /// <summary>
        /// 是否完成，没有完成说明是被取消了
        /// </summary>
        public bool IsCompleted { get; set; }

        /// <summary>
        /// 如果没有完成，理由
        /// </summary>
        public String Reason { get; set; }
    }

    /// <summary>
    /// 下载进度改变事件
    /// </summary>
    public class DownloadProgressChangedEventArgs : EventArgs
    {
        /// <summary>
        /// 当前进度
        /// </summary>
        public int Current { get; set; }

        /// <summary>
        /// 总进度
        /// </summary>
        public int Total { get; set; }
    }
}
